home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / Calculator / Calculator.m < prev    next >
Text File  |  1995-06-12  |  19KB  |  1,038 lines

  1. //
  2. // Calculator.m
  3. // Copyright (c) 1990, 1991,1992 by Jiro Nakamura 
  4. // All rights reserved
  5. //
  6. // Maintains a simple 4++ function calculator in Objective-C
  7. // Doesn't handle algebraic notation or simple ordering.
  8. //
  9. //    by Jiro Nakamura (jiro@shaman.com)
  10. //
  11. // RCS Information
  12. // Revision Number->    $Revision: 1.8 $
  13. // Last Revised->    $Date: 92/02/02 18:24:36 $
  14. //
  15. static char rcsid[] = "$Id: Calculator.m,v 1.8 92/02/02 18:24:36 jiro Exp Locker: jiro $";
  16.  
  17. #import <appkit/Application.h>
  18. #import <appkit/Button.h>    
  19. #import <appkit/Matrix.h>    
  20. #import <appkit/Panel.h>
  21. #import <appkit/Pasteboard.h>
  22. #import <appkit/publicWraps.h>      /* for NXBeep( )  */
  23. #import <appkit/ScrollView.h>  
  24. #import "appkit/TextField.h"
  25. #import <strings.h>
  26. #import "Calculator.h"
  27. #import <math.h>
  28. #import <signal.h>
  29. #import <libc.h>
  30.  
  31. #define OP_NOOP        0
  32. #define OP_ADD        1
  33. #define OP_SUBTRACT    2
  34. #define OP_MULTIPLY    3
  35. #define    OP_DIVIDE    4
  36. #define OP_LOGICAL_OR    5
  37. #define OP_LOGICAL_AND    6
  38. #define OP_LOGICAL_EOR    7    
  39. #define OP_LOGICAL_NOR    8
  40. #define OP_POWER    9
  41.  
  42. #define SHIFTMASK    (NX_ALTERNATEMASK | NX_SHIFTMASK)
  43.  
  44. char *OperatorDesc[10] =
  45.     {
  46.     "=",
  47.     "+",
  48.     "-",
  49.     "x",
  50.     "/",
  51.     "|",
  52.     "&",
  53.     "eor",
  54.     "nor",
  55.     "^"};
  56.     
  57.  
  58. #define CALCULATOR_ICON                "window.calculator.tiff"
  59. #define    CALCULATOR_TITLE_SHORT            "T.I. 68040"
  60. #define CALCULATOR_TITLE_LONG            "Tennessee Instruments"
  61.  
  62.  
  63. #define DM_BINARY    2
  64. #define DM_OCTAL    8
  65. #define DM_DECIMAL    10
  66. #define DM_HEXADECIMAL    16
  67.  
  68. // The minimum dimensions of the window
  69. #define MIN_WIDTH            210.0
  70. #define MIN_HEIGHT            350.0
  71.  
  72.  
  73. // Together, these two declarations form the floating point exception
  74. // part of Calculator
  75. id me;        // Class global attachment to self set in +new
  76. BOOL fpError;    // Global floating point error indicator
  77.  
  78. void floatingError(int sig)
  79. {
  80.     [me errorDisplay: "- Floating point error"];
  81.     fpError = YES;
  82.     return;
  83. }
  84.  
  85. // Math operators
  86. double magic( double y, int op, double x)
  87. {
  88.     switch( op)
  89.         {
  90.         case OP_ADD:
  91.             return y + x;
  92.         case OP_SUBTRACT:
  93.             return y - x ;
  94.         case OP_MULTIPLY:
  95.             return y * x;
  96.         case OP_DIVIDE:
  97.             if( x == 0.0)
  98.                 fpError = YES;
  99.             else
  100.                 return y / x;
  101.         case OP_LOGICAL_OR:
  102.             return (int) y | (int) x;
  103.         case OP_LOGICAL_AND:
  104.             return (int) y & (int) x;
  105.         case OP_LOGICAL_EOR:
  106.             return (int) y ^ (int) x;
  107.         case OP_POWER:
  108.             return pow( y, x);
  109.         case OP_NOOP:
  110.             return y;
  111.         default:
  112.             NXRunAlertPanel("Unrecognizable math.",
  113.                 "Contact Jiro: jiro@shaman.com",
  114.                  "OK",NULL, NULL);
  115.         }
  116.     return 0.0;
  117. }
  118.  
  119. // Not the most efficient (timewise) but....
  120. // Note: x should be an integer.
  121. double factorial(double x)
  122. {
  123.     double y;
  124.  
  125.     if( x <= 1.0)
  126.         return 1.0;
  127.     
  128.     if( x >= 170)    // Larger than this overflows a double
  129.         {
  130.         [me errorDisplay: "- Overflow"];
  131.         return -1;
  132.         }
  133.                     
  134.     y = rint(x);
  135.     x = 1;
  136.     
  137.     while( y )
  138.         x *= y--;
  139.  
  140.     return x;    
  141. }
  142.  
  143. int readBinary( const char *buf )
  144. {
  145.     int tmp = 0;
  146.     const char *pt = buf;
  147.     
  148.     for( ; *pt != '\0' && (*pt == '0' || *pt == '1'); pt ++ )
  149.         tmp = (tmp << 1) + (*pt - '0') ;
  150.     return tmp;
  151. }
  152.  
  153. char * writeBinary(char *buf, int val )
  154. {
  155.     int tmp;
  156.     
  157.     for( tmp = 15; tmp >= 0; tmp -- )
  158.         {
  159.         buf[15 - tmp] = (val & (1 << tmp) ) ? '1' : '0';
  160.         }
  161.     buf[16] = '\0';
  162.  
  163.     return buf;
  164. }
  165.  
  166.  
  167. @implementation Calculator
  168.  
  169. - initCalc
  170. {    
  171.     #ifdef DEBUG
  172.         fprintf(stderr,"Initing calculator object\n");
  173.     #endif
  174.  
  175.     if( calcDidInit == YES)
  176.         return self;
  177.         
  178.     m = 0.0;
  179.     x = 0.0;
  180.     y = 0.0;
  181.     x_isNew = YES;
  182.     displayMode = DM_DECIMAL;
  183.     x_hasDecimal = NO;
  184.     currentOperation = OP_NOOP;
  185.     [self updateOperationMarker];
  186.     fpError = NO;
  187.     signal( SIGFPE, floatingError);
  188.     me = self;
  189.     calcDidInit = YES;
  190.     scrollText = [scrollDisplay docView];
  191.     [scrollText setAlignment: NX_RIGHTALIGNED];
  192.     
  193.     return self;
  194. }
  195.  
  196. - key_add:sender
  197. {
  198.     [self processPrevious];
  199.     currentOperation = OP_ADD;
  200.     [self updateOperationMarker];
  201.     return self;
  202. }
  203.     
  204. - key_subtract:sender
  205. {
  206.     [self processPrevious];
  207.     currentOperation = OP_SUBTRACT;
  208.     [self updateOperationMarker];
  209.     return self;
  210. }
  211.  
  212.  
  213. - key_multiply:sender
  214. {
  215.     [self processPrevious];        
  216.     currentOperation = OP_MULTIPLY;
  217.     [self updateOperationMarker];
  218.     return self;
  219. }
  220.  
  221. - key_divide:sender
  222. {
  223.     [self processPrevious];
  224.     currentOperation = OP_DIVIDE;
  225.     [self updateOperationMarker];
  226.     return self;
  227. }
  228.  
  229. - key_logicalOr: sender
  230. {
  231.     [self processPrevious];
  232.     currentOperation = OP_LOGICAL_OR;
  233.     [self updateOperationMarker];
  234.     return self;
  235. }
  236.  
  237. - key_logicalAnd: sender
  238. {
  239.     [self processPrevious];
  240.     currentOperation = OP_LOGICAL_AND;
  241.     [self updateOperationMarker];
  242.     return self;
  243. }
  244.  
  245. - key_logicalEor: sender
  246. {
  247.     [self processPrevious];
  248.     currentOperation = OP_LOGICAL_EOR;
  249.     [self updateOperationMarker];
  250.     return self;
  251. }
  252.  
  253.  
  254.  
  255. - key_clear:sender
  256. {
  257.     if( fpError)
  258.         {
  259.         NXBeep();
  260.         return self;
  261.         }
  262.  
  263.     x = 0;
  264.     x_hasDecimal = NO;
  265.     x_isNew = YES;
  266.     [self setDisplay];
  267.     return self;
  268. }
  269.  
  270. - key_number:sender
  271. {
  272.     char key;
  273.     static char tmp[80];
  274.  
  275.     // We cannot do this from IB and we do not have a 
  276.     // appDidInit method
  277.     [invisibleEnterKey setKeyEquivalent:3];
  278.     
  279.     if( fpError)
  280.         {
  281.         NXBeep();
  282.         return self;
  283.         }
  284.  
  285.     if( x_isNew)
  286.         {
  287.         [self clearDisplay];
  288.         x_isNew = NO;
  289.         }
  290.         
  291.     key =   *[[sender  selectedCell]title];
  292.     
  293.     #ifdef DEBUG
  294.         fprintf(stderr,"Key = %c\n", key);
  295.     #endif
  296.     
  297.     if( key == '.')    // Decimal point
  298.         if( x_hasDecimal || displayMode != DM_DECIMAL )
  299.             {
  300.             NXBeep();
  301.             return self;
  302.             }
  303.         else
  304.             x_hasDecimal = YES;
  305.     
  306.     if( key == '0' && *([display stringValue]) == '0'
  307.         && x_hasDecimal == NO && displayMode == DM_DECIMAL)
  308.         {
  309.         NXBeep();
  310.         return self;
  311.         }
  312.                     
  313.     strcpy( tmp, [display stringValue]);
  314.     strcat( tmp, [[sender  selectedCell]title]);
  315.     [display setStringValue: tmp];
  316.     [self getDisplay];
  317.     
  318.     return self;
  319. }
  320.  
  321. - key_log:sender
  322. {
  323.     if( fpError)
  324.         {
  325.         NXBeep();
  326.         return self;
  327.         }
  328.  
  329.     [self getDisplay];
  330.  
  331.     if( ([NXApp currentEvent]->flags) & SHIFTMASK)
  332.         {
  333.         [self setScrollOperation: "Ln" andNumber: x];
  334.         x = log( x );
  335.         }    
  336.     else
  337.         {
  338.         [self setScrollOperation: "Log" andNumber: x];
  339.         x = log10( x );
  340.         }
  341.     [self setDisplay];
  342.     [self setScrollOperation: "=" andNumber: x];
  343.         
  344.     return self;
  345. }
  346.  
  347. - key_squareRoot:sender
  348. {
  349.     if( fpError)
  350.         {
  351.         NXBeep();
  352.         return self;
  353.         }
  354.  
  355.     [self getDisplay];
  356.  
  357.     // alternate key pressed
  358.     if( ([NXApp currentEvent]->flags) & SHIFTMASK)
  359.         {
  360.         [self setScrollOperation: "square" andNumber: x];
  361.         x = pow( x, 2);
  362.         }    
  363.     else
  364.         {
  365.         [self setScrollOperation: "root" andNumber: x];
  366.         if( x >= 0.0)
  367.             x = sqrt(x);
  368.         else
  369.             {
  370.             [self    errorDisplay: "- Root of negative"];
  371.             return self;
  372.             }
  373.         }
  374.  
  375.     [self setDisplay];
  376.     [self setScrollOperation: "=" andNumber: x];
  377.         
  378.     return self;
  379. }
  380.  
  381. - key_memory:sender
  382. {
  383.     if( fpError)
  384.         {
  385.         NXBeep();
  386.         return self;
  387.         }
  388.  
  389.     [self getDisplay];
  390.     #ifdef DEBUG
  391.         fprintf(stderr, "Memory: Tag = %d, m = %f, x = %f\n",
  392.             [sender tag], m, x);
  393.     #endif
  394.     
  395.     switch( [[sender selectedCell] tag])
  396.         {
  397.         case 0:        // M+
  398.             m += x;
  399.             [self setScrollOperation: "M+" andNumber: x];
  400.             break;
  401.         case 1:        // M-
  402.             m -= x;
  403.             [self setScrollOperation: "M-" andNumber: x];
  404.             break;
  405.         case 2:        // M=
  406.             m = x;
  407.             [self setScrollOperation: "M=" andNumber: x];
  408.             break;
  409.         case 3:        // MR
  410.             x = m;
  411.             x_isNew = YES;
  412.             x_hasDecimal = NO;
  413.             [self setDisplay];
  414.             [self setScrollOperation: "MR" andNumber: x];
  415.             break;
  416.         case 4:        // MC
  417.             m = 0;
  418.             break;
  419.         default:
  420.             NXRunAlertPanel("Dumb Error", 
  421.                 "Key_memory returns false case. Why? "
  422.                 "Ask Jiro.", "OK", NULL, NULL);
  423.             break;
  424.         }
  425.     [self    updateMemoryMarker];
  426.     return self;
  427. }
  428.  
  429. - key_factorial:sender
  430. {
  431.     if( fpError)
  432.         {
  433.         NXBeep();
  434.         return self;
  435.         }
  436.  
  437.     [self getDisplay];
  438.     
  439.     if( ([NXApp currentEvent]->flags) & SHIFTMASK)
  440.         {
  441.         [self setScrollOperation: "1/" andNumber: x];
  442.  
  443.         if( x != 0.0)
  444.             x = 1/x;
  445.         else
  446.             {
  447.             [self errorDisplay: "- Division by zero"];
  448.             return self;
  449.             }
  450.         }
  451.     else
  452.         {
  453.     
  454.         [self setScrollOperation: "factorial" andNumber: x];
  455.         if( x != rint(x) )    // if x is a non-integer
  456.             {
  457.             [self errorDisplay: "- Non-integer !"]; 
  458.             return self;
  459.             }
  460.         
  461.         if( x < 0.0 )    // if x is  negative
  462.             {
  463.             [self errorDisplay: "- Negative !"]; 
  464.             return self;
  465.             }                
  466.         x = factorial(x);
  467.         if( fpError )
  468.             {
  469.             [self errorDisplay];
  470.             return nil;
  471.             }
  472.         }    
  473.  
  474.     [self setDisplay];
  475.     [self setScrollOperation: "=" andNumber: x];
  476.  
  477.     return self;
  478. }
  479.  
  480.     
  481.  
  482. - key_enter:sender
  483. {
  484.     #ifdef DEBUG
  485.         fprintf(stderr, "Enter key hit. y = %f. Op = %d.\n", y,
  486.             currentOperation);
  487.     #endif
  488.     
  489.     if( fpError)
  490.         {
  491.         NXBeep();
  492.         return self;
  493.         }
  494.     
  495.     [self getDisplay];
  496.  
  497.         
  498.     if( currentOperation != OP_NOOP)
  499.         {
  500.         [self setScrollOperation: OperatorDesc[currentOperation] 
  501.             andNumber: x];
  502.         x = magic(y, currentOperation, x);
  503.         if( fpError )
  504.             {
  505.             [self errorDisplay];
  506.             return nil;
  507.             }
  508.         currentOperation = OP_NOOP;
  509.         [self updateOperationMarker];
  510.         }
  511.     [self setDisplay];
  512.  
  513.     [self setScrollOperation: "=" 
  514.         andNumber: x];
  515.  
  516.     return self;
  517. }
  518.  
  519. - key_allClear:sender
  520. {
  521.     #ifdef DEBUG
  522.         fprintf(stderr,"All Clear\n");    
  523.     #endif
  524.     
  525.     x = y = 0.0;
  526.     x_isNew = YES;
  527.     x_hasDecimal = NO;
  528.     currentOperation = OP_NOOP;
  529.     [self updateOperationMarker];
  530.     fpError = NO;
  531.     [self setDisplay];
  532.  
  533.  
  534.     [scrollText setSel: [scrollText textLength] 
  535.         : [scrollText textLength]];
  536.     [scrollText replaceSel: "\nAC\n"];
  537.     [scrollText scrollSelToVisible];
  538.     [scrollText hideCaret];
  539.     [self makeFirstResponder: self];
  540.  
  541.     return self;
  542. }
  543.  
  544. - key_power:sender
  545. {
  546.     if( fpError)
  547.         {
  548.         NXBeep();
  549.         return self;
  550.         }
  551.  
  552.     // alternate key pressed
  553.     if( ([NXApp currentEvent]->flags) & SHIFTMASK)
  554.         {
  555.         [self setScrollOperation: "exp" andNumber: x];
  556.         [self getDisplay];
  557.         x = exp(x);
  558.         [self setDisplay];
  559.         [self setScrollOperation: "=" andNumber: x];
  560.         }    
  561.     else
  562.         {
  563.         [self processPrevious];
  564.         currentOperation = OP_POWER;
  565.         [self updateOperationMarker];
  566.         }
  567.         
  568.     return self;
  569. }
  570.  
  571. - key_negate:sender
  572. {
  573.     if( fpError || displayMode == DM_OCTAL || displayMode == DM_BINARY)
  574.         {
  575.         NXBeep();
  576.         return self;
  577.         }
  578.  
  579.  
  580.     [self getDisplay];
  581.  
  582.     if( ([NXApp currentEvent]->flags) & SHIFTMASK)
  583.         {
  584.         [self setScrollOperation: "abs" andNumber: x];
  585.         x = fabs(x);
  586.         }
  587.     else
  588.         {
  589.         [self setScrollOperation: "+/-" andNumber: x];
  590.         x = -x;
  591.         }
  592.         
  593.         
  594.     [self setDisplay];
  595.     [self setScrollOperation: "=" andNumber: x];
  596.     return self;
  597. }
  598.  
  599. - key_baseChanged: sender
  600. {
  601.     static char buf[30];
  602.     
  603.     switch( atoi( [[sender selectedCell]title]) )
  604.         {
  605.         case 2:    // Previous was base 2, so new is base 8
  606.             displayMode = DM_OCTAL;
  607.             [hexadecimalKeyMatrix setEnabled: NO];
  608.             [[sender selectedCell] setTitle: "8"];
  609.             
  610.             [decimalKeyMatrix setEnabled: YES];
  611.             [[decimalKeyMatrix findCellWithTag: 8] setEnabled: NO];
  612.             [[decimalKeyMatrix findCellWithTag: 9] setEnabled: NO];
  613.             [decimalPointKey setEnabled: NO];
  614.             break;
  615.         case 8:    // Previous was base 8, so new is base 10
  616.             displayMode = DM_DECIMAL;
  617.             [[sender selectedCell] setTitle: "10"];
  618.             [hexadecimalKeyMatrix setEnabled: NO];
  619.             [[decimalKeyMatrix findCellWithTag: 8] 
  620.                 setEnabled: YES];
  621.             [[decimalKeyMatrix findCellWithTag: 9] setEnabled: 
  622.                 YES];
  623.             [decimalPointKey setEnabled: YES];
  624.             break;
  625.         case 10: // Previous was base 10, so new is base 16
  626.             displayMode = DM_HEXADECIMAL;
  627.             [hexadecimalKeyMatrix setEnabled: YES];
  628.             [[sender selectedCell] setTitle: "16"];
  629.             [decimalPointKey setEnabled: NO];
  630.             break;
  631.         case 16: // Previous was base 16, so new is base 2            
  632.             displayMode = DM_BINARY;
  633.             [hexadecimalKeyMatrix setEnabled: NO];
  634.             [[sender selectedCell] setTitle: "2"];
  635.  
  636.             [decimalKeyMatrix setEnabled: NO];
  637.             [[decimalKeyMatrix findCellWithTag: 0] 
  638.                 setEnabled: YES];
  639.             [[decimalKeyMatrix findCellWithTag: 1] 
  640.                 setEnabled: YES];
  641.  
  642.             [decimalPointKey setEnabled: NO];
  643.             break;
  644.         default:
  645.             NXBeep();
  646.             return nil;
  647.         }
  648.     [self setDisplay];
  649.  
  650.     sprintf(buf, "\nChange to base %d", displayMode );
  651.     [scrollText setSel: [scrollText textLength] 
  652.         : [scrollText textLength]];
  653.     [scrollText replaceSel: buf];
  654.     [self setScrollOperation: "=" andNumber: x];
  655.  
  656.     [self getDisplay];
  657.     return self;
  658. }
  659.  
  660.  
  661.  
  662. - setDisplay
  663. {
  664.     static char displayBuf[20];
  665.     #ifdef DEBUG
  666.         fprintf(stderr,"Display is set to %f.\n", x);
  667.     #endif
  668.     
  669.     switch (displayMode )
  670.         {
  671.         case DM_BINARY:
  672.             writeBinary(displayBuf, x);
  673.             [display setStringValue: displayBuf];
  674.             break;
  675.         case DM_OCTAL:
  676.             sprintf( displayBuf, "%o", (unsigned int) x);
  677.             [display setStringValue: displayBuf];
  678.             break;
  679.         case DM_HEXADECIMAL:
  680.             sprintf( displayBuf, "%x", (unsigned int) x);
  681.             [display setStringValue: displayBuf];
  682.             break;
  683.         case DM_DECIMAL:
  684.         default:
  685.             [display    setDoubleValue: x];
  686.             break;
  687.         }
  688.         
  689.     x_isNew = YES;
  690.     [self    updateMemoryMarker];
  691.     [self makeFirstResponder: self];
  692.     return self;
  693. }
  694.  
  695. - clearDisplay
  696. {
  697.     [display    setStringValue: ""];
  698.     x_hasDecimal = NO;
  699.     x_isNew = YES;
  700.     return self;
  701. }
  702.  
  703. - errorDisplay
  704. {
  705.     [display    setStringValue: "Error"];
  706.     fpError = YES;
  707.     return self;
  708. }
  709.  
  710. - errorDisplay: (char *) errorString
  711. {
  712.     static char buf[80];
  713.     
  714.     fpError = YES;
  715.     sprintf(buf, "Error %s", errorString);
  716.     [display    setStringValue: buf];
  717.  
  718.     
  719.     sprintf(buf, "\nError %s", errorString);
  720.     [scrollText setSel: [scrollText textLength] 
  721.         : [scrollText textLength]];
  722.     [scrollText replaceSel: buf];
  723.     [scrollText scrollSelToVisible];
  724.     [scrollText hideCaret];
  725.     [self makeFirstResponder: self];
  726.  
  727.     return self;
  728. }
  729.  
  730. - (double) getDisplay
  731. {
  732.     unsigned int utmp;
  733.     
  734.     if( !calcDidInit )
  735.         return( x = 0.0);
  736.         
  737.     switch (displayMode)
  738.         {
  739.         case DM_BINARY:
  740.             x = readBinary([display stringValue] );
  741.             break; 
  742.         case DM_OCTAL:
  743.             sscanf([display stringValue], "%o", &utmp);
  744.             x = utmp;
  745.             break;
  746.         case DM_HEXADECIMAL:
  747.             sscanf([display stringValue], "%x", &utmp);
  748.             x = utmp;
  749.             break;
  750.         case DM_DECIMAL:
  751.         default:
  752.             x = [display doubleValue];
  753.             break;
  754.         }
  755.                     
  756.     return( x );
  757. }
  758.  
  759. - updateMemoryMarker
  760. {
  761.     if( m == 0.0 )
  762.         [memoryDisplay setStringValue: ""];
  763.     else
  764.         [memoryDisplay setStringValue: "M"];
  765.     return self;
  766. }
  767.  
  768. - updateOperationMarker
  769. {
  770.     const char* operation;
  771.         
  772.     switch( currentOperation )
  773.         {
  774.         case OP_ADD:
  775.             operation = "+";
  776.             break;
  777.         case OP_SUBTRACT:
  778.             operation = "-";
  779.             break;
  780.         case OP_MULTIPLY:
  781.             operation = "x";
  782.             break;
  783.         case OP_DIVIDE:
  784.             operation = "/";
  785.             break;
  786.         case OP_LOGICAL_OR:
  787.             operation = "OR";
  788.             break;
  789.         case OP_LOGICAL_AND:
  790.             operation = "AND";
  791.             break;
  792.         case OP_LOGICAL_EOR:
  793.             operation = "EOR";
  794.             break;
  795.         case OP_LOGICAL_NOR:
  796.             operation = "NOR";
  797.             break;
  798.         case OP_NOOP:
  799.         default:
  800.             operation = "  ";
  801.             break;
  802.         }
  803.     [operationDisplay setStringValue: operation];
  804.     return self;
  805. }
  806.             
  807.         
  808.  
  809. // Delegate stuff to ensure proper miniaturization behaviour
  810. - windowWillMiniaturize: sender
  811.     toMiniwindow: mini
  812. {
  813.     [self    setTitle:    CALCULATOR_TITLE_SHORT];
  814.     return self;
  815. }
  816.  
  817. - windowDidUpdate: sender
  818. {
  819.     if( calcDidInit != YES)
  820.         [self initCalc];
  821.     [self    setMiniwindowIcon: CALCULATOR_ICON];
  822.     [self makeFirstResponder: self];
  823.     return self;
  824. }
  825.     
  826. - windowDidDeminiaturize: sender
  827. {
  828.     [self    setTitle:    CALCULATOR_TITLE_LONG];
  829.     [self makeFirstResponder: self];
  830.     return self;
  831. }
  832.  
  833.  
  834. - processPrevious
  835. {
  836.  
  837.  
  838.     if( fpError)
  839.         {
  840.         NXBeep();
  841.         return self;
  842.         }
  843.  
  844.         
  845.     if( currentOperation != OP_NOOP)
  846.         {
  847.         [self getDisplay];
  848.  
  849.         [self setScrollOperation: OperatorDesc[currentOperation] 
  850.             andNumber: x];
  851.         
  852.         y = magic(y, currentOperation, x);
  853.         if( fpError )
  854.             {
  855.             [self errorDisplay];
  856.             return nil;
  857.             }
  858.             
  859.         x = y;
  860.         [self setDisplay];
  861.         x_isNew = YES;
  862.  
  863.         }
  864.     else
  865.         {
  866.         [self getDisplay];
  867.         y = x;
  868.         x_isNew = YES;
  869.         }
  870.  
  871.     [self setScrollOperation: "=" andNumber: x];
  872.     
  873.     return self;
  874. }    
  875.  
  876. - copy: sender
  877. {
  878.     static char pasteBuffer[80];
  879.     const char *types[] = {NXAsciiPboardType,NULL};
  880.     id pasteBoard;
  881.     
  882.     if( fpError)
  883.         {
  884.         NXBeep();
  885.         return self;
  886.         }
  887.  
  888.     strcpy( pasteBuffer, [display stringValue]);
  889.     
  890.     pasteBoard = [Pasteboard newName: NXGeneralPboard];
  891.     
  892.     [pasteBoard        declareTypes:    types
  893.                 num:        1
  894.                 owner:        NULL];
  895.                 
  896.     [pasteBoard         writeType: NXAsciiPboardType
  897.                 data:     pasteBuffer
  898.                 length:    (int) strlen(pasteBuffer)];
  899.                 
  900.     #ifdef DEBUG
  901.         fprintf(stderr, "Copied <%s> to the Pasteboard.\n",
  902.             pasteBuffer);
  903.     #endif
  904.     
  905.     fprintf(stderr, "Copy!\n");
  906.     return self;
  907. }
  908.  
  909. - paste: sender
  910. {
  911.     static char *bufPt;
  912.     int len;
  913.     int tmp;
  914.     const NXAtom *types;
  915.     id pasteBoard;
  916.     
  917.     if( fpError)
  918.         {
  919.         NXBeep();
  920.         return self;
  921.         }
  922.     
  923.     pasteBoard = [Pasteboard newName: NXGeneralPboard];
  924.     
  925.     types =     [pasteBoard    types]; 
  926.     for( tmp = 0; types[tmp] != NULL; tmp ++ )
  927.         if( strcmp( types[tmp], NXAsciiPboardType ) == 0 )
  928.             {
  929.             [pasteBoard     readType: NXAsciiPboardType
  930.                 data:     &bufPt
  931.                 length:    &len];        
  932.             
  933.             [display setStringValue: bufPt];
  934.             [self getDisplay];
  935.             [self setDisplay];
  936.             [self setScrollOperation: "Paste: " andNumber: x];
  937.             
  938.             #ifdef DEBUG
  939.                 fprintf(stderr, "Pasted <%s> (%d) from "
  940.                     "the Pasteboard.\n", bufPt,
  941.                         len);
  942.             #endif
  943.             
  944.             break;
  945.             }        
  946.     return self;
  947. }
  948.  
  949. - setScrollOperation: (char *) op andNumber: (double) val
  950. {
  951.     static char buffer[30], buf2[30];
  952.  
  953.     switch (displayMode )
  954.         {
  955.         case DM_BINARY:
  956.             writeBinary( buf2, val);
  957.             sprintf(buffer, "\n%s %s", op, buf2);
  958.             break;
  959.  
  960.         case DM_OCTAL:
  961.             sprintf( buffer, "\n%s %o", 
  962.                 op, (unsigned int) val);
  963.             break;
  964.         case DM_HEXADECIMAL:
  965.             sprintf( buffer, "\n%s %x",         
  966.                 op, (unsigned int) val);
  967.             break;
  968.         case DM_DECIMAL:
  969.         default:
  970.             sprintf( buffer, "\n%s %g", 
  971.                 op, val);
  972.             break;
  973.         }
  974.  
  975.     [scrollText setSel: [scrollText textLength] 
  976.         : [scrollText textLength]];
  977.     [scrollText replaceSel: buffer];
  978.     [scrollText hideCaret];
  979.     [scrollText scrollSelToVisible];
  980.     [self makeFirstResponder: self];
  981.  
  982.     return self;
  983. }
  984.  
  985.  
  986. - windowWillResize: (id) sender toSize: (NXSize *) size
  987. {
  988.     #ifdef DEBUG
  989.         fprintf(stderr,"Window would have resized to %f x %f.\n", 
  990.             size->width, size->height);
  991.     #endif
  992.     
  993.     if( size->width < MIN_WIDTH)
  994.         size->width = MIN_WIDTH;
  995.     if( size->height < MIN_HEIGHT)
  996.         size->height = MIN_HEIGHT;
  997.         
  998.     #ifdef DEBUG
  999.         fprintf(stderr,"Window will resize to %f x %f.\n", 
  1000.             size->width, size->height);
  1001.     #endif
  1002.     return self;
  1003. }
  1004.  
  1005. - (BOOL)    acceptsFirstResponder    {    return YES; }
  1006.  
  1007. - printPSCode: sender    { [scrollText  printPSCode: sender]; return self;}
  1008.  
  1009. // From CalculatorLab/MinusPanel.m
  1010. // When the user presses the minus sign on the keypad, the resulting character
  1011. // is number 45 from the Symbol character set, not a minus sign from the ASCII
  1012. // character set (which you would get by pressing the key to the right of the 
  1013. // zero key).  The Panel class ignores commandKeys from all sets except the 
  1014. // ASCII set, but we want the user to be able to depress the minus key on the
  1015. // keypad and have it act like the real minus key.  We can get around this
  1016. // problem by checking for this special minus sign and converting it into an
  1017. // ASCII minus sign before it reaches the regular commandKey: method
  1018. - (BOOL)commandKey:(NXEvent *)theEvent
  1019. {
  1020.     NXEvent localEvent;
  1021.     BOOL    symbolSet, minusSign;
  1022.     
  1023.     symbolSet = theEvent->data.key.charSet == NX_SYMBOLSET;
  1024.     minusSign = theEvent->data.key.charCode == 45;
  1025.  
  1026.     if (symbolSet && minusSign) {  /* check for minus */
  1027.     localEvent = *theEvent;
  1028.     localEvent.data.key.charSet = NX_ASCIISET;
  1029.     localEvent.data.key.charCode = '-';
  1030.     return [super commandKey:&localEvent];
  1031.     } else {
  1032.         return [super commandKey:theEvent];
  1033.     }
  1034. }
  1035.  
  1036.  
  1037. @end
  1038.